package jdklearning.concurrent.thread.interrupt;

import jdklearning.concurrent.thread.ThreadStatusLearning;

import java.lang.reflect.Field;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * jstack pid
 *
 * 1. 每个线程都有一个interrupt status标志位，用于表明当前线程是否处于中断状态
 * 2. 一般调用Thread.interrupt()会有两种处理方式
 * 遇到一个低优先级的block状态时，比如object.wait(),object.sleep(),object.join()。
 * 它会立马触发一个unblock解除阻塞，并throw一个InterruptedException。
 * 其他情况，Thread.interrupt()仅仅只是更新了status标志位。
 * 然后你的工作线程通过Thread.isInterrrupted()进行检查，可以做相应的处理，
 * 比如也throw InterruptedException或者是清理状态，取消task等。
 *
 * @author shenenlu 2021年03月19日 下午16:14:35
 */
public class LockSupportAndInterrupt {

    private static LockSupportAndInterrupt blocker = new LockSupportAndInterrupt();

    public static void main(String[] args) throws Exception {
        System.out.println(ThreadStatusLearning.pid());
//        notifyLockSupportTest();
//        parkTest();
//        interruptParkTest();
//        interruptSleepTest();
//        interruptWaitTest();


        interruptPackTest1();
    }


    /**
     * park()和unpark()不会有 “Thread.suspend和Thread.resume所可能引发的死锁” 问题
     * ，由于许可的存在，调用 park 的线程和另一个试图将其 unpark 的线程之间的竞争将保持活性。
     *
     * 如果调用线程被中断，则park方法会返回。同时park也拥有可以设置超时时间的版本。
     *
     * 需要特别注意的一点：park 方法还可以在其他任何时间“毫无理由”地返回
     * ，因此通常必须在重新检查返回条件的循环里调用此方法。
     * 从这个意义上说，park 是“忙碌等待”的一种优化，它不会浪费这么多的时间进行自旋
     * ，但是必须将它与 unpark 配对使用才更高效。
     * @throws InterruptedException
     */
    public static void interruptPackTest1() throws InterruptedException {
        Thread t = new Thread(){
            @Override
            public void run() {
                while (true){
//                    LockSupport.parkNanos(blocker, TimeUnit.SECONDS.toNanos(10));
                    LockSupport.park();
                    System.out.println("aaa");
                }
            }
        };

        t.start();
        Thread.sleep(200);
        t.interrupt();
        System.out.println("Stopping application...");
    }

    /**
     * LockSupport.park对象后，尝试获取Thread.blocker对象，调用其single唤醒
     * 不能唤醒
     *
     * @throws Exception
     */
    private static void notifyLockSupportTest() throws Exception {
        Thread t = doTest(new TestCallBack() {
            @Override
            public void callback() throws Exception {
                System.out.println("blocker");
                LockSupport.park(blocker);
                System.out.println("wakeup now!");
            }
            @Override
            public String getName() {
                return "notifyLockSupportTest";
            }
        });
        t.start(); // 启动读取线程
        Thread.sleep(150);
        synchronized (blocker) {
            Field field = Thread.class.getDeclaredField("parkBlocker");
            field.setAccessible(true);
            Object fBlocker = field.get(t);
            System.out.println(blocker == fBlocker);
            Thread.sleep(100);
            System.out.println("notifyAll");
            blocker.notifyAll();
        }
        System.out.println("Stopping application...");
    }


    /**
     * 尝试去中断一个object.wait()，会抛出对应的InterruptedException异常
     *
     * @throws InterruptedException
     */
    private static void interruptWaitTest() throws InterruptedException {
        final Object obj = new Object();
        Thread t = doTest(new TestCallBack() {
            @Override
            public void callback() throws Exception {
                obj.wait();
                System.out.println("wakeup now!");
            }
            @Override
            public String getName() {
                return "interruptWaitTest";
            }
        });
        t.start(); // 启动读取线程
        Thread.sleep(5000);
        t.interrupt(); // 检查下在park时，是否响应中断
        System.out.println("Stopping application...");
    }



    /**
     * 尝试去中断一个Thread.sleep()，会抛出对应的InterruptedException异常
     *
     * @throws InterruptedException
     */
    private static void interruptSleepTest() throws InterruptedException {
        Thread t = doTest(new TestCallBack() {
            @Override
            public void callback() throws Exception {
                // 尝试sleep 5s
                Thread.sleep(5000);
                System.out.println("wakeup now!");
            }
            @Override
            public String getName() {
                return "interruptSleepTest";
            }
        });
        t.start(); // 启动读取线程
        Thread.sleep(2000);
        t.interrupt(); // 检查下在park时，是否响应中断
        System.out.println("Stopping application...");
    }


    /**
     * 尝试去中断一个LockSupport.park()，会有响应但不会抛出InterruptedException异常
     *
     * @throws InterruptedException
     */
    private static void interruptParkTest() throws InterruptedException {
        Thread t = doTest(new TestCallBack() {
            @Override
            public void callback() {
                LockSupport.parkNanos(blocker, TimeUnit.SECONDS.toNanos(10));
                System.out.println("wakeup now!");
            }
            @Override
            public String getName() {
               return "interruptParkTest";
            }
        });
        t.start(); // 启动读取线程
        Thread.sleep(3000);
        t.interrupt(); // 检查下在park时，是否响应中断
        System.out.println("Stopping application...");
    }


    /**
    * 尝试去中断一个LockSupport.unPark()，会有响应
    *
    * @throws InterruptedException
    */
    private static void parkTest() throws InterruptedException {
        Thread t = doTest(new TestCallBack() {
            @Override
            public void callback() {
                LockSupport.park(blocker);
                System.out.println("wakeup now!");
            }
            @Override
            public String getName() {
                return "parkTest";
            }
        });
        t.start(); // 启动读取线程
        Thread.sleep(5000);
        LockSupport.unpark(t);
        t.interrupt();
        System.out.println("Stopping application...");
    }



    public static Thread doTest(final TestCallBack call) {
        Thread t =  new Thread() {
            @Override
            public void run() {
//                File file = new File("/dev/urandom"); // 读取linux黑洞
//                try {
//                    FileInputStream in = new FileInputStream(file);
//                    byte[] bytes = new byte[1024];
//                    while (in.read(bytes, 0, 1024) > 0) {
//                        if (Thread.interrupted()) {
//                            throw new InterruptedException("");
//                        }
//                        System.out.println(bytes[0]);
//                        Thread.sleep(100);
//                        long start = System.currentTimeMillis();
//                        call.callback();
//                        System.out.println(call.getName() + " callback finish cost : "
//                                + (System.currentTimeMillis() - start));
//                    }
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }

                try {
                    while (true){
                        if (Thread.interrupted()) {
                            throw new InterruptedException("manually throw InterruptedException");
                        }
                        System.out.println(System.nanoTime());
                        Thread.sleep(1200);
                        long start = System.currentTimeMillis();
                        call.callback();
                        System.out.println(call.getName() + " callback finish cost : "
                                + (System.currentTimeMillis() - start));
                    }
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        t.setName(call.getName());
        return t;
    }


}


interface TestCallBack {
    public void callback() throws Exception;
    public String getName();
}

